home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
038a
/
qbtree45.zip
/
QBTREE45.DOC
< prev
next >
Wrap
Text File
|
1991-01-03
|
53KB
|
2,971 lines
A B-Tree Access Method
for QuickBASIC Programmers.
QBTREE v4.50
Jan 3, 1991
(C)1989-1991 Cornel Huth
The QBTREE package is a shareware product. You may try QBTREE to
see if it fits your needs on a trial basis only. You may copy and
distribute this shareware package freely. If you plan to use it
after the trial, fill out the registration form and send it along
with payment of $45 to:
Cornel Huth
ATTN: QBTREE 4.50 REGISTRATION
6402 Ingram Rd.
San Antonio, TX 78238
Source code for the QBTREE interface will be sent upon receipt of
the completed registration form on the last page. This interface
is all that is needed to modify QBTREE for most situations since
all I/O is performed at this level (QuickBASIC). If you also need
the low-level btree routine, I'll make it available. It's written
in MASM assembly. (Contact me for more details.)
QBTREE requires QuickBASIC 4.00 or above. For file sharing and
record locking functions DOS 3.0 or above is needed.
WHAT IS IT?
QBTREE is a keyed-file system based on the b-tree sorting method.
It maintains up to 5 key files and also 5 data files at one time.
(Source is available to modify this to your needs; DOS 3.30 and
up allow you to use as many as 255 total files.) Not only does
QBTREE find any particular key and its data record very quickly
- any key from a million-key file in 1/10 second on a 18ms hard
disk (worst-case) - it also allows sequential inorder access to
the data file. The data file is automatically maintained and the
index file automatically balanced. Deletions in both the data and
key files are made available for reuse, thus making repacking
unnecessary.
The following files should be found in this shareware package:
QBTREE45.LIB standard btree library
NBTREE45.LIB network btree library
QBTREE45.BI btree routine function prototypes
TEST45.BAS barebones btree program using most routines
MEMCOPY.ASM assembly source for the MEMCOPY routine
QBTREE45.DOC this documentation
INSTALLATION
Add QBTREE45.LIB to your standard library if you like, and then
LINK /QU it into a QLB library for the environment.
C>lib yourstd.lib + QBTREE45.LIB;
C>link /qu yourstd.lib,qb.qlb,nul,bqlb4x.lib
You may prefer to keep QBTREE45.LIB as a separate library,
instead.
C>link /qu yourstd.lib+QBTREE45.LIB,qb.qlb,nul,bqlb4x.lib
Don't forget to invoke QB with the /l libraryname parameter.
QBTREE comes as two separate libraries. One for standard
applications, the other for network applications. If you need the
network support routines, substitute NBTREE45.LIB for
QBTREE45.LIB.
Note that INTERRUPT and INTERRUPTX are defined in the module
INTRPT within the QBTREE45 library. If you prefer to not have it
there but instead in your own library, remove it from
QBTREE45.LIB, e.g.,
C>lib QBTREE45.LIB -INTRPT;
Otherwise, you may see a linker error about INTERRUPT and
INTERRUPTX having already been defined. If you would rather not
see these messages add the /NOE option to link. Note that QB
4.00b, and 4.50 have a bug in the interrupt code when doing
interrupts 25/26h. QB 4.00 has this bug and another that causes
the passed di register to not be properly passed. I recommend
that if you use INTERRUPT(X) that you replace you INTRPT code
with the INTRPT code included with QBTREE45 whether you intend to
use QBTREE or not. To do that:
C>lib QBTREE45 *INTRPT; {first make a copy of it}
C>lib QB.LIB -+INTRPT; {then replace bad with good}
C>link /qu QB.LIB... {and make a new QLB}
When linking a stand-alone program include the library in the
link process by adding QBTREE45.LIB to the libraries prompt from
LINK.EXE. If you are using a compiler other than BC 4.00b, LINK
will ask for the path to BCOM41.LIB. For the shareware evaluation
you can enter the path to your run-time library. If you are using
BC 4.00 the name is \path\BCOM40.LIB. If you are using BC 4.5 the
name is \path\BCOM45.LIB. With registration you receive the
source, ready to be compiled by your compiler version. Thereafter
LINK won't prompt you for the run-time library path.
INTERFACE SUMMARY
All QBTREE routines are FUNCTIONs and return an integer code
which is detailed in ERROR CODES. These must be DECLAREd.
Use REM $INCLUDE:"QBTREE45.BI" in your programs.
1) AddRecord% (kfile%, dfile%, Qkey$, Qrec$)
2) CloseDataFile% (dfileno%)
3) CloseKeyFile% (kfileno%)
4) CreateDataFile% (filename$, recl%)
5) CreateKeyFile% (filename$, keyl%)
6) DeleteKey% (kfile%, Qkey$)
7) DeleteRecord% (kfile%, dfile%, Qkey$)
8) FlushDataFile% (dfileno%, dup%)
9) FlushKeyFile% (kfileno%, dup%)
10) GetDirect% (dfileno%, recno&, Qrec$)
11) GetEqual% (kfile%, dfile%, Qkey$, Qrec$)
12) GetFirst% (kfile%, dfile%, Qkey$, Qrec$)
13) GetLast% (kfile%, dfile%, Qkey$, Qrec$)
14) GetNext% (kfile%, dfile%, Qkey$, Qrec$)
15) GetPosition% (kfileno%, recno&)
16) GetPrev% (kfile%, dfile%, Qkey$, Qrec$)
17) OpenDataFile% (filename$, dfileno%)
18) OpenKeyFile% (filename$, kfileno%)
19) PutKey% (kfile%, dfile%, Qkey$)
20) QBTreeVer% (ver%)
21) StatDataFile% (dfileno%, recl%, recs&, bfileno%)
22) StatKeyFile% (kfileno%, keyl%, keys&, bfileno%)
23) UpdateRecord% (dfile%, Qrec$)
These are network support routines available in NBTREE45.LIB.
24) LoadDataHeader% (dfileno%)
25) LoadKeyHeader% (kfileno%)
26) LockDataHeader% (dfileno%)
27) LockKeyFile% (kfileno%)
28) LockRecord% (dfileno%, recno&)
29) UnlockDataHeader% (dfileno%)
30) UnlockKeyFile% (kfileno%)
31) UnlockRecord% (dfileno%, recno&)
INTERFACE DETAIL
1) AddRecord(kfile,dfile,Qkey$,Qrec$)
Add the key, Qkey$, to the key file, kfile, and the data record,
Qrec$, to the data file, dfile. Qkey$ must not already exist.
QBTREE is case-sensitive so it is recommended that keys be made
upper-case (or lower) unless there is a reason not to do this.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
Qkey$ STRING. Key to add to key file.
Qrec$ STRING. Data record to add to the data file
indexed by Qkey$.
DEFINT A-Z
kfile = 0
dfile = 0
Qkey$ = acctid$ + acctxn$
Qrec$ = xaction$
stat = AddRecord(kfile,dfile,Qkey$,Qrec$)
2) CloseDataFile(dfile)
Close the data file, dfile, releasing the data buffer allocated
to it. This function is essential for proper termination in
QBTREE. Release any active locks first.
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
dfile = 0
stat = CloseDataFile(dfile)
3) CloseKeyFile(kfile)
Close the key file, kfile. This is essential for proper
termination in QBTREE. Header information is written only when
the file is closed, or flushed. Release any active locks first.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
kfile = 0
stat = CloseKeyFile(kfile)
4) CreateDataFile(filename$,reclen)
Create a new data file. filename$ must not already exist.
filename$ STRING. Pathname of data file to create. Any
valid DOS drive/path/filename can be used.
reclen INTEGER. Length of record for this data file.
Valid range is 3 to 32767 bytes, though QB's
string space will be the limiting factor.
filename$ = "C:\HIST\AR89.DAT"
reclen = 128
stat = CreateDataFile(filename$,reclen)
5) CreateKeyFile(filename$,keylen)
Create a new key file. filename$ must not already exist.
filename$ STRING. Pathname of key file to create. Any
valid DOS drive/path/filename can be used.
keylen INTEGER. Length of key for this key file. Valid
range is 1 to 64 bytes.
filename$ = "C:\HIST\AR89.KEY"
keylen = 16
stat = CreateKeyFile(filename$,keylen)
6) DeleteKey(kfile,Qkey$)
Delete the key, Qkey$, from kfile. The data record associated
with Qkey$ is not affected. This is necessary since you may have
other indexes pointing to the data record.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
Qkey$ STRING. Key to remove from kfile.
kfile = 0
Qkey$ = acctid$ + acctxn$
stat = DeleteKey(kfile,Qkey$)
7) DeleteRecord(kfile,dfile,Qkey$)
Delete the key, Qkey$, from file kfile and also delete the
associated record from dfile.
This function deletes Qkey$ only in kfile and also delete the
data record that Qkey$ had pointed to in dfile. If you have other
indexes pointing to this data record, use DeleteKey() on those
indexes before doing this function.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
Qkey$ STRING. Key for which you want to delete from
the key file, kfile, and also delete its
associated data record in dfile.
kfile = 0
dfile = 0
Qkey$ = acctid$ + acctxn$
stat = DeleteRecord(kfile,dfile,Qkey$)
8) FlushDataFile(dfile,dup)
Updates the data header information and, if dup is non-zero,
causes DOS to update the directory entry for dfile. This is done
by writing the data header out, having DOS duplicate dfile's
handle, then closing the duplicate handle. This does not have the
overhead of having to re-open the data file. This also flushes
DOS's internal buffers.
In LAN applications you should perform this function before
releasing a data record lock, but dup must be equal to 0. If you
force DOS to update the directory entry, UnlockRecord() returns
error 229 - Lock already in force. Not flushing the buffers is
not a problem in LAN applications since the SHARE.EXE will not
allow 'local buffer' problems to occur.
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
dup INTEGER. Non-zero to force directory update.
dfile = 0
dup = -1 'dup must be 0 for LAN applications
stat = FlushDataFile(dfile,dup)
Note: Flushing buffers should be done whenever processing can
handle the delay. Once a file has been flushed (and no further
changes have been made), a power-outage can bring your system
down with no ill-effects to the QBTREE file - all will be intact.
9) FlushKeyFile(kfile,dup)
Updates the key header information and, if dup is non-zero,
causes DOS to update the directory entry for kfile. This is done
by writing the key header out, having DOS duplicate file's
handle, then closing the duplicate handle. This does not have the
overhead of having to re-open the key file. This also flushes
DOS's internal buffers.
In LAN applications you should perform this function before
releasing a key file lock, but dup must be equal to 0. If you
force DOS to update the directory entry, UnlockKeyFile() returns
error 229 - Lock already in force. Not flushing the buffers is
not a problem in LAN applications since the SHARE.EXE will not
allow 'local buffer' problems to occur.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
dup INTEGER. Non-zero to force directory update.
kfile = 0
dup = -1 'dup must be 0 for LAN applications
stat = FlushKeyFile(kfile,dup)
10) GetDirect(dfile,recno&,Qrec$)
Return the data in dfile at the logical record number, recno&.
This is especially useful for reindexing data files, provided
that the key is imbedded in the data record. It is possible to
retrieve deleted records from the data file; records are not
physically deleted but are marked 'available' for a new data
record. See the data record format below. It is also possible to
give recno& a value past the true end of file. This function does
not affect the index file's internal pointers.
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
recno& LONG. Record number to seek (1-based).
Qrec$ STRING. Returned record data.
dfile = 0
recno& = 456&
stat = GetDirect(dfile,recno&,Qrec$)
if stat = 0 then
print "Record#";recno&;" =";Qrec$
11) GetEqual(kfile,dfile,Qkey$,Qrec$)
Search for key, Qkey$, in kfile, and if found, retrieve the data
record from dfile, and place it in Qrec$. If not found, the
internal pointers indicate where it would have been. By using
GetNext(), GetPrev(), the next or previous ordered key and data
can be obtained.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
Qkey$ STRING. Key for which to search in key file.
Qrec$ STRING. Returned data record associated with
Qkey$.
kfile = 0
dfile = 0
Qkey$ = acctid$ + acctxn$
stat = GetEqual(kfile,dfile,Qkey$,Qrec$)
'{Qrec$ is read}
if stat = 200 then
stat = GetNext(kfile,dfile,Qkey$,Qrec$)
'{Qkey$ & Qrec$ are read}
12) GetFirst(kfile,dfile,Qkey$,Qrec$)
Retrieve the first ordered key in kfile, placing it in Qkey$, and
place its data record from dfile into Qrec$.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
Qkey$ STRING. Returned first ordered key.
Qrec$ STRING. Returned data record associated with
returned key, Qkey$.
kfile = 0
dfile = 0
stat = GetFirst(kfile,dfile,Qkey$,Qrec$)
13) GetLast(kfile,dfile,Qkey$,Qrec$)
Retrieve the last ordered key in kfile, placing it in Qkey$, and
place its data record from dfile into Qrec$.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
Qkey$ STRING. Returned last ordered key.
Qrec$ STRING. Returned data record associated with
returned key, Qkey$.
kfile = 0
dfile = 0
stat = GetLast(kfile,dfile,Qkey$,Qrec$)
14) GetNext(kfile,dfile,Qkey$,Qrec$)
Retrieve the next ordered key in kfile, placing it in Qkey$, and
place its data record from dfile into Qrec$. This function allows
for sequential inorder processing of the data file.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
Qkey$ STRING. Returned key which immediately follows
the key found (or not found) in GetEqual() or
GetFirst().
Qrec$ STRING. Returned data record associated with
returned key, Qkey$.
kfile = 0
dfile = 0
Qkey$ = acctid$ + acctxn$
stat = GetNext(kfile,dfile,Qkey$,Qrec$)
15) GetPosition(kfile,recno&)
Return the logical record number of the current record pointed to
by the last key FOUND in kfile. This function can be used to
match keys from different key files that relate to a data file by
recno& only. (Useful in deleting non-relational keys.)
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
recno& LONG. Returned current record number in the
last used (current) data file.
kfile = 0
stat = GetPosition(kfile,recno&)
if stat = 0 then
print "Current data record of kfile";kfile;" is";recno&
16) GetPrev(kfile,dfile,Qkey$,Qrec$)
Retrieve the previous ordered key in kfile, placing it in Qkey$,
and place its data record from dfile into Qrec$. This function
allows for reverse sequential inorder processing of the data
file.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
Qkey$ STRING. Returned key which immediately precedes
the key found (or not found) in GetEqual() or
GetLast().
Qrec$ STRING. Returned data record associated with
returned key, Qkey$.
kfile = 0
dfile = 0
Qkey$ = acctid$ + acctxn$
stat = GetPrev(kfile,dfile,Qkey$,Qrec$)
17) OpenDataFile(filename$,fileno)
Open an EXISTING data file and associate it with fileno and
allocate a data buffer the size of the datafile's record length.
The fileno is a number you choose between 0 to 4. This number is
used to reference the data file in any later operation. The
maximum files can be increased or decreased by changing the MDF
constant in the interface source.
If version 3.0 or greater of DOS is detected by QBTREE then the
open is performed with a READ/WRITE access and DENY NONE sharing.
Older versions of DOS will have a normal open call (COMPATIBLE).
What this means is that other processes may read from and write
to the data file, filename$. See the LockRecord() and
UnlockRecord() functions for details on locking the data file.
filename$ STRING. Pathname of existing data file.
fileno INTEGER. Number to associate the data file with
for future operations. Valid range is 0 to 4.
{See OpenKeyFile() for an example.}
18) OpenKeyFile(filename$,fileno)
Open an EXISTING key file and associate it with fileno. Before
*USING* the key file, a data file will also need to be opened.
You may have multiple index files opened for a data file.
The fileno is a number you choose between 0 to 4. This number is
used to reference the key file in any later operation. The
maximum files can be increased or decreased by changing the MKF
constant in the interface source.
If version 3.0 or greater of DOS is detected by QBTREE then the
open is performed with a READ/WRITE access and DENY NONE sharing.
Older versions of DOS will have a normal open call (COMPATIBLE).
What this means is that other processes may read from and write
to the key file, filename$. See the LockKeyFile() and
UnlockKeyFile() functions for details on locking the key file.
filename$ STRING. Pathname of existing key file.
fileno INTEGER. Number to associate the key file with
for future operations. Valid range is 0 to 4.
filename$ = "C:\HIST\AR89."
ARKEY = 0
ARDAT = 0
stat = OpenKeyFile(filename$+"KEY",ARKEY)
if stat = 0 then stat = OpenDataFile(filename$+"DAT",ARDAT)
if stat <> 0 then DoErrorProc stat
19) PutKey(kfile,dfile,Qkey$)
After having found a key with GetFirst(), GetLast(), GetPrev(),
GetEqual(), GetNext(), or AddRecord(), insert the key Qkey$ into
the key file, kfile (which most likely is an index file other
than the one where the key was found, though it can be the same).
Associated with it the data record that was indexed by the found
key. This allows you have multiple indexes per data file. If an
error code is returned you must *REACCESS* the original key (with
the GETx()) before attempting to re-PutKey() error 206, invalid
data pointer, is returned. If error 201, key already exists, is
returned, and you want to allow duplicate keys, you need to add
an enumerator to the key. See NON-UNIQUE KEYS below.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
Qkey$ STRING. Key for which you want to add to the key
file, kfile, and have it also point to the data
record in dfile whose key was last found.
stat = GetEqual(0,0,Qkey$,Qrec$)
if stat = 0 then
enum = 0
enum$ = mki$(enum) 'make it a string
'need to reverse enum$ bytes to sort low to high
'use a function in your code such as SwapByte$(),etc
ZKey$ = NewKey$ + right$(enum$,1) + left$(enum$,1)
stat = PutKey(1,0,ZKey$)
else
stop
endif
if stat = 201 then 'PutKey() key exists error
'reset the internal pointer to a valid record
stat = GetEqual(0,0,Qkey$,Qrec$)
'look for the absolute last one (won't be found=200)
stat = GetEqual(1,0,left$(Zkey$,len(ZKey$-2)+_
chr$(255)+chr$(255),Zrec$)
'so get the last valid one
stat = GetPrev(1,0,Zkey$,Zrec$)
enum$ = right$(ZKey$,2)
enum$ = right$(enum$,1)+left$(enum$,1)
enum = cvi(enum$) 'make it an integer
'inc the enumerator
enum = enum + 1
enum$ = mki$(enum)
ZKey$ = NewKey$ + right$(enum$,1) + left$(enum$,1)
stat = PutKey(1,0,ZKey$)
20) QBTreeVer(ver)
Return the version of the QBTREE access method (X100).
ver INTEGER. Returned version number * 100.
stat = QBTreeVer(ver)
print "QBTREE version";ver\100
21) StatDataFile(dfile,reclen,recs&,bfileno)
Return information about the data file, dfile.
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
reclen INTEGER. Returned length of records for dfile.
recs& LONG. Returned number of records in dfile.
bfileno INTEGER. Returned BASIC's file number for dfile.
dfile = 0
stat = StatDataFile(dfile,reclen,recs&,bfileno)
22) StatKeyFile(kfile,keylen,keys&,bfileno)
Return information about the key file, kfile.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
keylen INTEGER. Returned length of keys for kfile.
keys& LONG. Returned number of keys in kfile.
bfileno INTEGER. Returned BASIC's file number for kfile.
kfile = 0
stat = StatKeyFile(kfile,keylen,keys&,bfileno)
23) UpdateRecord(dfile,Qrec$)
Overwrite the current data record in dfile with Qrec$. This
allows you to update the contents of a record in a data file. Do
not use this if you change an imbedded key in this record. If you
want to change a key, first AddRecord() the new data and if a-ok,
delete the old one (DeleteRecord()).
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
Qrec$ STRING. Data to use in replacing the previous
data in dfile.
kfile = 0
dfile = 0
Qkey$ = acctid$
stat = GetEqual(kfile,dfile,Qkey$,Qrec$)
if stat = 0 then
Qrec$ = newQrec$
stat = UpdateRecord(dfile,Qrec$)
24) LoadDataHeader(dfile)
Load the header information for the data file, dfile, from
disk. This should be used in networking applications whenever
a file has been released to other processes.
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
{See LoadKeyHeader() for an example.}
25) LoadKeyHeader(kfile)
Load the header information for the key file, kfile, from disk.
This should be used in networking applications whenever a file
has been released to other processes. Note that internal pointers
will probably have changed. A GetEqual() to the last processed
record will need to be used followed by a GetNext() for
sequential inorder processing.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
kfile = 0
dfile = 0
stat = LockKeyFile(kfile)
if not stat then
stat = LockDataHeader(dfile)
if not stat then
stat = LockRecord(dfile,0&)
if not stat then
stat = LoadKeyHeader(kfile)
stat = LoadDataHeader(dfile)
26) LockDataHeader(dfile)
Lock the data header in dfile to the current process. Any other
process requesting access to the header will be denied
permission. This function should be used before adding or
deleting a data record, though not needed for updating an
existing record. To perform this DOS LAN function, the DOS
program SHARE.EXE must be run.
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
dfile = 0
stat = LockDataHeader(dfile)
if stat = 229 then
print "Lock already in force"
elseif stat = 232 then
print "SHARE.EXE needs to be run"
27) LockKeyFile(kfile)
Lock the key file, kfile, to the current process. Any other
process requesting access to kfile will be denied permission. It
is recommended that this be used, in LAN applications, prior to
AddRecord(), PutKey(), DeleteKey(), DeleteRecord(). To perform
this DOS LAN function, the DOS program SHARE.EXE needs to be run.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
kfile = 0
stat = LockKeyFile(kfile)
if stat = 229 then
print "Lock already in force"
elseif stat = 232 then
print "SHARE.EXE has not been run"
28) LockRecord(dfile,recno&)
Lock a specific record, recno&, in dfile, to the current process.
Any other process requesting access to recno& will be denied
permission. If recno&=0& then all records in dfile will be locked
(records 1 to EOF plus). To perform this DOS LAN function, the
DOS program SHARE.EXE must be run. Note that you must use
LockDataHeader() to lock the header record.
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
recno& LONG. Data record number in data file to lock.
dfile = 0
recno& = 1& '{lock the first record in dfile}
stat = LockRecord(dfile,recno&)
if stat = 229 then
print "Lock already in force"
elseif stat = 232 then
print "SHARE.EXE needs to be run"
29) UnlockDataHeader(dfile)
Unlock the data header in dfile. Commit the file header with a
FlushDataFile() first. To perform this DOS LAN function, the DOS
program SHARE.EXE must be installed.
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
dfile = 0
stat = UnlockDataHeader(dfile)
if stat = 229 then
print "Lock already in force/cannot DUP LAN..."
elseif stat = 232 then
print "SHARE.EXE needs to be run"
30) UnlockKeyFile(kfile)
Unlock the key file, kfile. You should do this as soon as you
have committed the file with FlushKeyFile() after a AddRecord(),
PutKey(), DeleteKey(), DeleteRecord(). To perform this DOS LAN
function, the DOS program SHARE.EXE needs to be run.
kfile INTEGER. Number that was used as fileno in
OpenKeyFile().
kfile = 0
stat = UnlockKeyFile(kfile)
if stat = 232 then
print "SHARE.EXE has not been run"
31) UnlockRecord(dfile,recno&)
Unlock a specific record, recno&, in dfile. Once the record that
was locked is no longer needed, you should unlock it. If a
process terminates without releasing active locks on a file, the
result is undefined. If recno&=0& then all records in dfile will
be unlocked (records 1 to EOF plus). To perform this DOS LAN
function, the DOS program SHARE.EXE must be installed.
dfile INTEGER. Number that was used as fileno in
OpenDataFile().
recno& LONG. Data record number in data file to
unlock.
dfile = 0
recno& = 1& '{unlock the first record in dfile}
stat = UnlockRecord(dfile,recno&)
if stat = 232 then
print "SHARE.EXE has not been run"
ERROR CODES
200 Key not found
- the key is not in the index file. This will occur during a
GetEqual(), DeleteKey(), DeleteRecord().
201 Key already exists
- duplicate keys are not allowed in QBTREE. This will occur
during AddRecord(), PutKey(). If this error occurs with
PutKey(), you must re-establish a valid data pointer before
trying again.
202 End of file
- GetNext() has reached the end of file.
203 Top of file
- GetPrev() has reached the top of file.
204 Empty file
- there are no keys in the index file. This will occur when
you attempt to perform functions that would require a non-
empty file.
205 Disk full
- if the key file drive and the data file drive are the same
a minimum of 2 clusters are needed for the key file plus the
clusters required to completely hold the data record before
you can AddRecord() or PutKey(). If they're separate drives,
the key file needs at least 2 clusters free on its disk and
the data file disk as many clusters as needed to hold the
data record. This will also occur during CreateKeyFile(),
CreateDataFile().
206 Data pointer invalid
- a valid QBTREE key access, either GetEqual(), GetNext(),
GetPrev(), GetFirst(), GetLast() must occur right before a
data record can be UpdateRecord() or a key can be PutKey().
207-209 reserved
210 Stack overflow (10 levels)
- the internal tracking stack exceeded capacity. This should
never occur.
211 Function not implemented
- this won't happen unless you call the low-level driver.
212-218 reserved
219 File number invalid
- the file number for the keyfile or datafile is greater
than the highest allocated in BTREE45.BAS. Increase the
MKF/MDF maximum files constant in the interface source.
220 Data record length invalid
- length must be from 3-32767 bytes when creating data file.
221 Key length invalid
- length must be from 1-64 bytes when creating key file.
222 File not open
- the fileno for a key or data file operation is not opened.
223 Invalid null key assignment
-a null key cannot be used. Null is defined in QBTREE as
either ASCII 0 or ASCII 255. This will occur in AddRecord(),
Delete(), GetEqual(), PutKey(). This pertains only to the
first character of the key. This will also occur if you
reach top of file and attempt to Delete().
224 Invalid record number
- a record number less than 1 was used in GetDirect(). Upper
bounds is not validated.
225 No handle available for DUP
- no DOS handles are available to flush files. Use a greater
value in config.sys FILES= (FILES=20).
226 Invalid drive specifier
- the drive specified in CreateKeyFile(), CreateDataFile()
is not a valid DOS drive.
227 reserved
228 File not QBTREE
- the filename in OpenKeyFile() or OpenDataFile() is not
recognized as a QBTREE file.
229 Lock already in force
- the lock requested cannot be made because an existing lock
of the header, record or file is in force. See FlushFile().
230 File already exists
- the CreateKeyFile() or CreateDataFile() filename already
exists. Delete the filename if you really want to use the
same filename.
231 File not found
- the filename$ specified in OpenKeyFile() or OpenDataFile()
does not exist or is not valid.
232 General lock failure
- usually means that SHARE.EXE was not run before using the
network functions.
233-255 reserved
TECHNICAL SPECIFICATIONS
Key length : 1-64 bytes (ASCII sort), constant
Record length : 3-32767 bytes, constant (available near memory)
Node size : 512 bytes
Keys per node : 7-84 keys, (512-3)\(key length+5)
Max keys/file : 5.5 million keys (65535 nodes)
Max datafile : 16 million bytes
Max key files : 5 opened at one time (* to 255 total w/source)
Max data files : 5 opened at one time (* and DOS 3.30 and up)
Key file header format (first sector (512 bytes) of key file):
filetype char ; 0 file type, "*", ASCII 42
rootnode int ; 1 sector in file of root node
nokeyslo int ; 3 number of keys (low word)
nokeyshi byte ; 5 number of keys (high byte)
keyavsec int ; 6 key available list head
nxkeysec int ; 8 next free sector
keylen byte ; 10 length of key
maxkeys byte ; 11 maximum keys per node
ackysc int ; 12 actual key sector in buffer
cukysc int ; 14 current key sector
cukyof byte ; 16 current key offset
iflag byte ; 17 internal flag
stkcnt byte ; 18 tree level count
pstack long ; 19 - 58 tree stack (4 bytes per level)
reserved any ; 59 - 255
free use any ; 256 - 383
reserved any ; 384 - 511
Data file header format (first 32 bytes of data file):
filetype char ; 0 file type, "S"
reclen int ; 1 length of record
norecslo int ; 3 number of records (low word)
norecshi byte ; 5 number of records (high byte)
datavlo int ; 6 data available list (low word)
datavhi byte ; 8 data available list (high byte)
nxdalo int ; 9 next data record avail (low word)
nxdahi byte ; 11 next data record avail (high byte)
internal byte ; 12 internal flag
cudalo int ; 13 current data record (low word)
cudahi byte ; 15 current data record (high byte)
internal any ; 16 - 31 reserved
There is an internal first key with a null value immediately
following the key header.
Beginning each node (sector) is a count key byte. This is the
count of keys on that sector. Then for each key is a 16-bit
previous node pointer, the key itself, the 24-bit data pointer
for that key, and a 16-bit next node pointer (node pointers are
zero at the leaf nodes).
02 00 00 000000 00 00 00 00 00 KEY001 01 00 00 00 00 ...
1. 2. 3. 4. 5. 6. 7. 8. 9.
1. Key count for that node
2. Node back pointer (for non-leaf nodes)
3. The internal null key (in least-valued node)
4. The 24-bit data record pointer (LB/HB/XB)
5. Forward ptr/back ptr (for non-leaf nodes)
6. First logical key
7. Its data pointer (record number in data file)
8. Its forward pointer (for non-leaf nodes)
9. repeat 3 to 8 for each key on node
QBTREE makes some allowances on the strict B-TREE and ISAM
definitions. For all practical purposes, consider QBTREE superior
to the text-book implementations of these.
Data record format:
Straight data after the header. Logical record #1 follows the
header. Records that have been deleted have the first 3 bytes
used as a link to the records available list. datavlo/hi in the
header point to the last deleted record (or 0 if none). The first
3 bytes of that deleted record point to the previously deleted
record, and so on. When 3 zero bytes form the pointer, that
record is the last in the list of deletes. Any additional records
then will expand the file.
By reserving the first 3 bytes of the data record it would be
easy to reindex a datafile. Set the bytes to all ASCII 255. You
then use GetDirect(), check to see if the 3 bytes are still 255s,
and if so, AddRecord() to a new key and data file. If not 255s,
you know that that record is deleted.
COMPILER INFORMATION and RESOURCE ALLOCATION
BTREE45.BAS, the I/O and interface portion of QBTREE, was written
and compiled with QuickBASIC 4.00b. The BTREE45.BAS file was
compiled with the /O option only. If using QuickBASIC 4.00b+,
which has a better error handling algorithm than 4.0, you can
trap I/O errors with QB even though the BTREE45 module was
compiled without the /E or /X options. However, version 4.00 will
dump the program to DOS on errors. So if your are using version
4.00, you need to perform common I/O checks for fatal errors such
as checking to see that the drive is ready etc., before using
QBTREE functions with QB/BC 4.0.
Storage for the data buffers and headers are allocated in the
default data area of QB. BTREE45.BAS storage allocation is
as follows (with default MKF and MKD = 4):
2 integer arrays (0 to 4) for file handles.
1 key hdr array (0 to 4) for key file headers, ea 62 bytes.
1 data hdr array (0 to 4) for data file headers, ea 32 bytes.
1 key node array (0 to 4) for node buffers, ea 512 bytes.
1 data buffer array$ (0 to 4) for data record I/O, size allocated
is equal to that dfile's record length (var-len$).
1 long integer array (0 to 4) to track current record numbers.
2 integer arrays (0 to 4) to track file drives.
1 type variable for interfacing with QBTREE low-level, 20 bytes.
... and other miscellaneous variables (see the source for more)
USING TYPED VARIABLES
QBTREE uses a simple variable, namely a var-len string, to get
and put data in the data file. To use QB TYPEd variables, you may
want to use this method. Setup your TYPEd variable, then allocate
enough space in a var-len string to copy to and from it.
TYPE dataTYPE
TAG AS STRING * 4
SSN AS STRING * 9
AGE AS INTEGER
XXX AS STRING * 1
END TYPE '16
DIM SSNage AS dataTYPE
SSNage.TAG = ""
SSNage.SSN = ssn$
SSNage.AGE = age
DIM SHARED work$
work$ = SPACE$(LEN(SSNage))
FromSec = VARSEG(SSNage)
FromOff = VARPTR(SSNage)
ToSec = VARSEG(work$) '{we want the offset to the string's}
ToOff = SADD(work$) '{address, not the string descriptor's}
count = len(work$)
dir = 1
MemCopy FromSec,FromOff,ToSec,ToOff,count,dir
'{You now have the TYPEd variable data in a var-len string that}
'{can be used as the Qrec$. To put Qrec$ in the typed variable,}
'{reverse the From/To assignments.}
{MemCopy is included in the QBTREE45.LIB and QBTREE45.BI}
NON-UNIQUE KEYS
QBTREE doesn't allow duplicate keys (this may be an inconvenience
in some data base programming) but this can easily be worked
around by adding to each key an additional two bytes. These bytes
would act to differentiate up to 65536 'identical' keys.
For example, if the needed key length is 16 bytes, make it 18.
Reserve bytes 17 and 18 so that you can enumerate identical keys.
When adding a key that doesn't already exist, set bytes 17 and 18
to ASCII 0. If the key already exists (error 201), use the
highest possible enumerator (ASCII 255,255) for the key. Do a
GetEqual(). Since that key will not be found (error 200), do a
GetPrev(). This retrieves the very last inorder record for the
key. Add 1 to the found enumerator value and use that as the
enumerator for the next key. Add the key.
Note: For proper sorting, you should have the most significant
byte of the enumerator first and the least significant last. See
the PutKey() code example for more.
PDS QBX ENVIRONMENT
QBX, the PDS 7 environment, uses far strings exclusively. QBTREE
works with strings that are known to be in the default data area
DGROUP. Because of this, QBTREE is not compatible with the QBX
environment. It is compatible with the PDS BC when far strings
are not used.
With the BTREE45.BAS source, a skilled programmer could simulate
the data structures needed in DGROUP (using COMMON, or writing a
routine to allocate data from the stack). Once that's done,
QBTREE could be used in the QBX environment.
****************** QBTREE REGISTRATION FORM ********** 450 ***
Name of registrant: __________________________________________
Amount paid: $ ____________ ($45 ea.) If you want me to send
the interface source
code, fill out the non-
release form below.
Comments: ___________________________________________________
_____________________________________________________________
Contact: ________________________ Phone:( )____-__________
Would you like to be added to our mailing list? ______________
Mail to: ___________________________________________________
______________________________________________________________
______________________________________________________________
Send this page with payment to:
Cornel Huth
ATTN: QBTREE 4.50 REGISTRATION
6402 Ingram Rd.
San Antonio, TX 78238
*** NON-RELEASE FORM for QBTREE 4.50 INTERFACE SOURCE CODE ****
I, _______________________________________, hereby agree not
to distribute, nor claim ownership of, QBTREE source code, or
any modification to the QBTREE interface, and not remove the
copyright notice of Cornel Huth from any part of QBTREE.
DATE:__________________
_______________________
(If you would like the interface source code in QB 4.00+,
print your name in the first blank, date the second, and
sign the third.)